/**
 * @file ts.c
 * @brief 提供从触摸屏设备读取坐标的功能
 * @details 该文件包含了用于处理来自触摸屏输入设备（如 /dev/input/eventX）的原始事件数据，
 * 并将其解析为屏幕坐标的函数。
 * @date 2025-07-07
 */
#include <stdio.h>
#include <unistd.h>      // 提供 read() 系统调用
#include <linux/input.h> // 定义 input_event 结构体以及事件类型和编码的宏 (如 EV_ABS, ABS_X)
#include "ts.h"          // 包含 postion 结构体的定义

/**
 * @brief 从触摸屏设备读取并尝试解析一个触摸事件。
 * @details 此版本根据诊断日志进行适配：它不依赖EV_SYN同步信号。
 * 只要连续收到了X和Y坐标，就认为是一次有效的触摸事件。这是一个简化的状态机。
 * @param fd 已打开的触摸屏设备的文件描述符。
 * @param pos_out 一个指向 struct postion 的指针，用于接收最终的坐标。
 * @return int 1表示成功获取到一个完整坐标，0表示还未获取完整坐标，-1表示读取错误。
 */
int get_touch_event(int fd, struct postion *pos_out)
{
    // 定义静态变量来保存状态。'static' 关键字使这些变量在函数调用之间保持其值。
    static struct postion current_pos = {-1, -1}; // 用于暂存正在接收的坐标
    static int x_ready = 0; // 状态标志位：0表示未收到X，1表示已收到X，正在等待Y

    struct input_event ev; // 用于存储从设备读取的原始事件数据

    // 从文件描述符读取一个 input_event 结构体大小的数据
    ssize_t bytes_read = read(fd, &ev, sizeof(ev));
    // 检查读取是否成功，一个完整的事件应该是 sizeof(struct input_event) 字节
    if (bytes_read != sizeof(struct input_event)) {
        // 如果读取失败或数据不完整，重置状态机并返回错误
        x_ready = 0;
        perror("read touch event error");
        return -1;
    }

    // ev.type == 3 (EV_ABS) 表示这是一个绝对位移事件，触摸屏通常使用此类型
    // ev.code == 0 (ABS_X) 表示这是 X 轴的坐标数据
    if (ev.type == EV_ABS && ev.code == ABS_X) {
        current_pos.x = ev.value; // 将事件的 value 存为 X 坐标
        x_ready = 1;              // 更新状态：已收到X
        return 0;                 // 返回0，表示坐标还不完整，需要继续读取Y坐标
    }
    // ev.type == 3 (EV_ABS) 绝对位移事件
    // ev.code == 1 (ABS_Y) 表示这是 Y 轴的坐标数据
    else if (ev.type == EV_ABS && ev.code == ABS_Y) {
        // 关键检查：只在已经接收到 X 坐标之后，才处理 Y 坐标
        if (x_ready) {
            current_pos.y = ev.value; // 将事件的 value 存为 Y 坐标
            *pos_out = current_pos;   // 此时 X 和 Y 都已收到，将完整坐标拷贝到输出参数

            // **核心**：成功获取一个完整坐标后，必须立刻重置状态机，
            // 以便准备下一次的 "先X后Y" 的接收流程。
            x_ready = 0;

            return 1; // 返回1，表示成功获取一个完整坐标！
        }
    }
    
    // 如果是其他类型的事件（如 EV_KEY, EV_SYN），或者是在收到X之前就收到了Y，
    // 则忽略它们，返回0，表示继续等待有效的事件序列。
    return 0;
}


/**
 * @brief (旧函数) 阻塞式地获取一个触摸屏坐标
 * @details 此函数在一个无限循环中调用 get_touch_event，直到成功获取到一个
 * 完整的坐标 (get_touch_event 返回 1) 才返回。
 * @param fd 已打开的触摸屏设备的文件描述符。
 * @return struct postion 返回获取到的坐标。
 */
struct postion ts_xy(int fd)
{
    struct postion pos;
    // 持续循环，直到 get_touch_event 报告成功
    while(1)
    {
        // 如果 get_touch_event 返回1，表示已成功将坐标写入 pos
        if (get_touch_event(fd, &pos) == 1)
        {
            // 成功获取，跳出循环并返回坐标
            return pos;
        }
        // 如果返回 0 或 -1，则循环继续，尝试读取下一个事件
    }
}